package com.laizhenghua.share.device.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.laizhenghua.share.common.core.context.SecurityContextHolder;
import com.laizhenghua.share.common.core.domain.R;
import com.laizhenghua.share.common.core.exception.ServiceException;
import com.laizhenghua.share.device.constant.PowerBankStatus;
import com.laizhenghua.share.device.domain.entity.*;
import com.laizhenghua.share.device.domain.vo.AvailablePowerBankVo;
import com.laizhenghua.share.device.domain.vo.ScanChangeVo;
import com.laizhenghua.share.device.domain.vo.StationVo;
import com.laizhenghua.share.device.emqx.EmqxClientWrapper;
import com.laizhenghua.share.device.emqx.constant.EmqxConstants;
import com.laizhenghua.share.device.service.*;
import com.laizhenghua.share.order.api.RemoteOrderService;
import com.laizhenghua.share.order.api.domain.entity.OrderInfo;
import com.laizhenghua.share.rule.api.RemoteRuleService;
import com.laizhenghua.share.rule.api.domain.entity.FeeRule;
import com.laizhenghua.share.user.api.RemoteUserService;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * TODO
 *
 * @Description
 * @Author laizh
 * @Date 2025/7/19 18:19
 **/
@Service
public class IDeviceServiceImpl implements IDeviceService {
    private static final Logger log = LoggerFactory.getLogger(IDeviceServiceImpl.class);
    private final MongoTemplate mongoTemplate;
    private final IStationService iStationService;
    private final ICabinetService iCabinetService;
    private final RemoteRuleService remoteRuleService;
    private final IMapService iMapService;
    private final RemoteUserService remoteUserService;
    private final RemoteOrderService remoteOrderService;
    private final EmqxClientWrapper emqxClientWrapper;
    private final ICabinetSlotService cabinetSlotService;
    private final IPowerBankService powerBankService;

    @Autowired
    public IDeviceServiceImpl(
            MongoTemplate mongoTemplate,
            IStationService iStationService,
            ICabinetService iCabinetService,
            RemoteRuleService remoteRuleService,
            IMapService iMapService,
            RemoteUserService remoteUserService,
            RemoteOrderService remoteOrderService,
            EmqxClientWrapper emqxClientWrapper,
            ICabinetSlotService cabinetSlotService,
            IPowerBankService powerBankService) {
        this.mongoTemplate = mongoTemplate;
        this.iStationService = iStationService;
        this.iCabinetService = iCabinetService;
        this.remoteRuleService = remoteRuleService;
        this.iMapService = iMapService;
        this.remoteUserService = remoteUserService;
        this.remoteOrderService = remoteOrderService;
        this.emqxClientWrapper = emqxClientWrapper;
        this.cabinetSlotService = cabinetSlotService;
        this.powerBankService = powerBankService;
    }

    @Override
    public R<List<StationVo>> nearbyStation(String longitude, String latitude, Integer radius) {
        GeoJsonPoint geoJsonPoint = new GeoJsonPoint(Double.parseDouble(longitude), Double.parseDouble(latitude)); // 中心点
        Distance distance = new Distance(radius, Metrics.KILOMETERS); // 圆的半径 5km
        Circle circle = new Circle(geoJsonPoint, distance);
        Query query = Query.query(Criteria.where("location").withinSphere(circle));
        List<StationLocation> stationLocations = mongoTemplate.find(query, StationLocation.class);
        if (CollectionUtil.isEmpty(stationLocations)) {
            return R.fail(null, StrUtil.format("{}公里内未查询到可用站点!", radius));
        }
        List<Long> stationIdList = stationLocations.stream().limit(15).map(StationLocation::getStationId).toList();
        LambdaQueryWrapper<Station> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Station::getId, stationIdList);
        List<Station> stationList = iStationService.list(wrapper);
        List<StationVo> list = stationList.stream().map(station -> getStationVo(longitude, latitude, station)).toList();
        return R.ok(list);
    }

    @Override
    public R<StationVo> stationDetail(Long id, String longitude, String latitude) {
        Station station = iStationService.getById(id);
        return R.ok(getStationVo(longitude, latitude, station));
    }

    @Override
    public R<ScanChangeVo> scanChange(String cabinetNo) {
        Long userId = SecurityContextHolder.getUserId();
        /*
        Long userId = SecurityContextHolder.getUserId();
        // 1.查询用户信息
        R<UserInfo> userInfoRes = remoteUserService.getUserInfo(userId);
        if (!userInfoRes.isSuccess()) {
            return R.fail(userInfoRes.getMsg());
        }
        */
        // 2.是否免押金查询
        R<Boolean> freeDepositRes = remoteUserService.isFreeDeposit();
        if (!freeDepositRes.isSuccess()) {
            return R.fail(freeDepositRes.getMsg());
        }
        if (!freeDepositRes.getData()) {
            throw new ServiceException("未申请免押金使用");
        }
        // 3.查询未完成的订单
        R<OrderInfo> notFinishOrderRes = remoteOrderService.findNotFinishOrder();
        if (!notFinishOrderRes.isSuccess()) {
            return R.fail(notFinishOrderRes.getMsg());
        }
        ScanChangeVo scanChangeVo = getScanChangeVo(notFinishOrderRes);
        if (!"1".equals(scanChangeVo.getStatus())) {
            return R.ok(scanChangeVo);
        }
        // 4.获取可用的充电宝信息
        AvailablePowerBankVo availablePowerBank = checkAvailablePowerBank(cabinetNo);
        if (null == availablePowerBank) {
            return R.fail("当前柜机无可用的充电宝");
        }
        String errMessage = availablePowerBank.getErrMessage();
        if (StrUtil.isNotEmpty(errMessage)) {
            return R.fail(errMessage);
        }
        // 5.硬件对接 - 把充电宝弹出柜机
        JSONObject command = new JSONObject(6); // mqtt指令
        command.put("uId", userId);
        command.put("mNo", StrUtil.format("mm{}", RandomUtil.randomString(8)));
        command.put("cNo", cabinetNo);
        command.put("pNo", availablePowerBank.getPowerBankNo());
        command.put("sNo", availablePowerBank.getSlotNo());
        String topic = String.format(EmqxConstants.TOPIC_SCAN_SUBMIT, cabinetNo);
        emqxClientWrapper.publish(topic, command.toJSONString());
        scanChangeVo.setStatus("1");
        return R.ok(scanChangeVo);
    }

    @NotNull
    private ScanChangeVo getScanChangeVo(R<OrderInfo> notFinishOrderRes) {
        ScanChangeVo scanChangeVo = new ScanChangeVo();
        OrderInfo notFinishOrder = notFinishOrderRes.getData();
        if (null == notFinishOrder) {
            scanChangeVo.setStatus("1"); // 成功
            scanChangeVo.setMsg("success");
        } else {
            String status = notFinishOrder.getStatus();
            if ("0".equals(status)) {
                scanChangeVo.setStatus("2");
                scanChangeVo.setMsg("有未归还的充电宝，请归还后再使用。");
            } else {
                scanChangeVo.setStatus("3");
                scanChangeVo.setMsg("有未支付的订单，请先完成支付。");
            }
        }
        return scanChangeVo;
    }

    private StationVo getStationVo(String longitude, String latitude, Station station) {
        StationVo stationVo = new StationVo();
        BeanUtils.copyProperties(station, stationVo);
        Double mapDistance = iMapService.calculateDistance(longitude, latitude, station.getLongitude().toString(),
                station.getLatitude().toString());
        stationVo.setDistance(mapDistance);
        // 站点费用规则
        Long feeRuleId = station.getFeeRuleId();
        R<FeeRule> result = remoteRuleService.detail(feeRuleId);
        if (ObjectUtil.isNotNull(result) && result.isSuccess()) {
            FeeRule data = result.getData();
            stationVo.setFeeRule(data.getDescription());
        }
        Long cabinetId = station.getCabinetId(); // 柜机
        Cabinet cabinet = iCabinetService.getById(cabinetId);
        if (ObjectUtil.isNotNull(cabinet)) {
            Integer availableNum = cabinet.getAvailableNum(); // 当前柜机充电宝可用数量
            if (availableNum > 0) {
                stationVo.setIsUsable("1");
            } else {
                stationVo.setIsUsable("0");
            }
            Integer freeSlots = cabinet.getFreeSlots(); // 当前柜机空闲插槽数量(充电宝可归还数量)
            if (freeSlots > 0) {
                stationVo.setIsReturn("1");
            } else {
                stationVo.setIsReturn("0");
            }
        } else {
            stationVo.setIsUsable("0");
            stationVo.setIsReturn("0");
        }
        return stationVo;
    }

    private AvailablePowerBankVo checkAvailablePowerBank(String cabinetNo) {
        Cabinet cabinet = iCabinetService.getCabinetByNo(cabinetNo);
        // 柜机可用的充电宝数量判断
        Integer availableNum = cabinet.getAvailableNum();
        if (ObjectUtil.isNull(availableNum) || availableNum == 0) {
            return null;
        }
        AvailablePowerBankVo availablePowerBankVo = new AvailablePowerBankVo();
        Long cabinetId = cabinet.getId(); // 柜机id
        List<CabinetSlot> cabinetSlotList = cabinetSlotService.listCabinetSlotByCabinetId(cabinetId);
        Set<Long> powerbankIdList = cabinetSlotList.stream()
                .filter(item -> null != item.getCabinetId())
                .map(CabinetSlot::getPowerBankId)
                .collect(Collectors.toSet());

        List<PowerBank> powerBanks = powerBankService.list(new LambdaQueryWrapper<PowerBank>()
                .in(PowerBank::getId, powerbankIdList)
                .eq(PowerBank::getStatus, PowerBankStatus.K.getStatus())
        );
        if (CollectionUtil.isEmpty(powerBanks)) {
            availablePowerBankVo.setErrMessage("当前柜机无可用充电宝");
            return availablePowerBankVo;
        }
        // 电量降序排序
        powerBanks.sort((a, b) -> b.getElectricity().compareTo(a.getElectricity()));
        PowerBank powerBank = powerBanks.get(0); // 最优充电宝
        Optional<CabinetSlot> optional = some(cabinetSlotList, item -> null != item.getPowerBankId() && item.getPowerBankId().equals(powerBank.getId()));
        if (optional.isEmpty()) {
            availablePowerBankVo.setErrMessage("获取插槽信息失败，请稍后重试！");
            return availablePowerBankVo;
        }
        // 锁定插槽
        CabinetSlot cabinetSlot = optional.get();
        cabinetSlot.setStatus("2");
        cabinetSlotService.updateById(cabinetSlot);
        availablePowerBankVo.setPowerBankNo(powerBank.getPowerBankNo());
        availablePowerBankVo.setSlotNo(cabinetSlot.getSlotNo());
        return availablePowerBankVo;
    }

    private <T> Optional<T> some(Iterable<T> iterable, Predicate<? super T> predicate) {
        if (null != iterable) {
            for (T t : iterable) {
                if (predicate.test(t)) {
                    return Optional.of(t);
                }
            }
        }
        return Optional.empty();
    }
}
